package com.jqing.lib.http;

import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;

import com.android.volley.*;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.*;
import com.jqing.lib.print.Print;

import org.json.JSONArray;
import org.json.JSONObject;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Created by chshqiang on 14-12-1.
 * api基类
 */
public abstract class Xapi<T> {
    /**
     * 返回数据结构为{@link com.google.gson.JsonObject}
     */
    public static final int TYPE_OBJECT = 0;
    /**
     * 返回数据结构为{@link com.google.gson.JsonArray}
     */
    public static final int TYPE_ARRAY = 1;
    /**
     * 返回数据结构为{@link String}
     */
    public static final int TYPE_STRING = 2;

    /**
     * 返回数据回调接口
     */
    protected ApiCallBack<T> callBack;

    /**
     * 请求连接
     */
    protected String url;
    protected String api;
    protected String params;
    /**
     * 请求参数
     */
    protected Map<String, String> mRequestBody;
    private RequestQueue queue;

    /**
     * 只实现该构造函数时，必须要重写{@link #start()}方法
     */
    protected Xapi(Context context) {
        if (context instanceof Application) {
            queue = Volley.newRequestQueue(context);
        } else {
            queue = Volley.newRequestQueue(context.getApplicationContext());
        }
    }

    /**
     * 只实现该构造函数时，必须要重写{@link #start()}方法
     */
    protected Xapi(Context context, ApiCallBack<T> callBack) {
        this(context);

        this.callBack = callBack;
    }

    public Xapi(Context context, ApiCallBack<T> callBack, String domain, String api) {
        this(context);

        url = domain + api;
        this.api = api;

        this.callBack = callBack;
    }

    /**
     * 设置参数
     *
     * @param params 参数集合
     */
    public Xapi setParams(Map<String, String> params) {
        if (null != params && !params.isEmpty()) {
            int size = params.size();
            mRequestBody = new HashMap<String, String>(size);
            mRequestBody.putAll(params);
        }

        return this;
    }

    /**
     * 获取请求返回数据结构类型
     *
     * @return 数据结构类型，见{@link #TYPE_OBJECT}、{@link #TYPE_ARRAY}，默认为{@link #TYPE_OBJECT}
     */
    public int getType() {
        return TYPE_OBJECT;
    }

    /**
     * 获取是否缓存
     *
     * @return true: 是
     */
    public boolean isCache() {
        return false;
    }

    /**
     * @return 获取该接口在缓存中的key值
     */
    public String getCacheKey() {
        return api;
    }

    /**
     * 获取缓存{@link android.content.SharedPreferences}对象
     *
     * @return {@link android.content.SharedPreferences}对象
     */
    public SharedPreferences getSharedPreferences() {
        return null;
    }

    /**
     * @return 获取该接口最新返回的缓存数据
     */
    public String getCacheData() {
        if (isCache()) {
            return getSharedPreferences().getString(getCacheKey(), "");
        }

        return "";
    }

    public void valueOfParams() {
        params = "/";
        Set<String> keys = mRequestBody.keySet();

        for (String key : keys) {
            String kv = key + "=" + mRequestBody.get(key) + "&";
            params += kv;
        }

        params = params.substring(0, params.length() -1);
    }

    /**
     * 开始执行
     */
    public void start() {
        valueOfParams();
        String cacheData = getCacheData();

        if (!TextUtils.isEmpty(cacheData)) {
            onCallback(cacheData);
        }

        Request request = null;
        switch (getType()) {
            case TYPE_STRING:
                request = new StringRequest(Request.Method.POST, url, mRequestBody, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        onCallback(response);
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        onFailure(error);
                    }
                });
                break;
            case TYPE_ARRAY:
                request = new MapJsonArrayRequest(Request.Method.POST, url, mRequestBody,
                        new Response.Listener<JSONArray>() {
                            @Override
                            public void onResponse(JSONArray response) {
                                onCallback(response.toString());
                            }
                        },
                        new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                onFailure(error);
                            }
                        }
                );
                break;
            case TYPE_OBJECT:
            default:
                request = new MapJsonObjectRequest(Request.Method.POST, url, mRequestBody,
                        new Response.Listener<JSONObject>() {
                            @Override
                            public void onResponse(JSONObject response) {
                                onCallback(response.toString());
                            }
                        },
                        new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                onFailure(error);
                            }
                        }
                );
                break;
        }
        request.setRetryPolicy(new DefaultRetryPolicy(15 * 1000, 3, 1.0f));

        queue.add(request);
        queue.start();
    }

    private void onFailure(VolleyError error) {
        Print.e(api + params + ": " + error.getMessage());
        error.printStackTrace();
        callBack.onFailure(error.fillInStackTrace(), 0, error.getMessage());
    }

    private void onCallback(String json) {
        T t = parseJson(json);

        if (null == t) {
            Print.e(api + params + ": NULL");//
            callBack.onFailure(null, 0, "没有数据");
        } else {
            if (isCache()) {
                SharedPreferences preferences = getSharedPreferences();
                preferences.edit().putString(getCacheKey(), json).commit();
            }

            Print.m(api + params + "\r\n" + t.toString());
            callBack.onSuccess(t);
        }
    }

    /**
     * 取消请求
     */
    public void cancel() {
        queue.cancelAll(new RequestQueue.RequestFilter() {
            @Override
            public boolean apply(Request<?> request) {
                return true;
            }
        });
        queue.stop();
    }

    public void clear() {
        mRequestBody.clear();

        queue = null;
        mRequestBody = null;

    }

    private Class<T> getTClass() {
        Type t = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) t).getActualTypeArguments();
        Class<T> cls = (Class<T>) params[0];

        return cls;
    }

    private Type getTType() {
        Type t = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) t).getActualTypeArguments();
        return params[0];
    }

    public T parseJson(String json, Class<T> clazz) {
        if (null == json) return null;

        try {
            GsonBuilder builder = new GsonBuilder();
            Gson gson = builder
                    .registerTypeAdapter(Date.class,
                            new DateDeserializerUtils())
                    .setDateFormat(DateFormat.LONG).create();

            return gson.fromJson(json, clazz);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public T parseJson(String json) {
        if (null == json) return null;

        try {
            GsonBuilder builder = new GsonBuilder();
            Gson gson = builder
                    .registerTypeAdapter(Date.class,
                            new DateDeserializerUtils())
                    .setDateFormat(DateFormat.LONG).create();

            return gson.fromJson(json, getTType());

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public class DateDeserializerUtils implements JsonDeserializer<Date> {
        public Date deserialize(JsonElement json, Type type,
                                JsonDeserializationContext context) throws JsonParseException {

            return new Date(json.getAsJsonPrimitive().getAsLong());

        }
    }


}
